/**
 * Copyright 2007-2008. Chongqing First Information & Network Co., Ltd. All rights reserved.
 * <a>http://www.cqfirst.com.cn</a>
 */
package com.gmail.blackdog1987.onedollar.util;

import java.beans.PropertyEditor;
import java.beans.PropertyEditorManager;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

/**
 * @author liuhz create 2007-9-28
 */
public class IntrospectionUtils {
	/**
	 * 获取指定对象target属性放入map中,并在key中添加指定prefix
	 * 
	 * @param target 指定对象实例
	 * @param props 放回map
	 * @param prefix 指定属性前缀
	 * @return true 当成功获取一个以上属性
	 */
	public static boolean getProperties(Object target, Map<String, Object> props, String prefix) {
		boolean rc = false;

		checkArguments(target, props);

		if (prefix == null) {
			prefix = "";
		}
		Class<?> clazz = target.getClass();
		Method[] methods = clazz.getMethods();
		for (int i = 0; i < methods.length; i++) {
			Method method = methods[i];
			String name = method.getName();
			Class<?> type = method.getReturnType();
			Class<?> params[] = method.getParameterTypes();
			if (name.startsWith("get") && params.length == 0 && type != null && isSettableType(type)) {
				try {
					Object value = method.invoke(target, new Object[] {});
					if (value == null) {
						continue;
					}
					String strValue = convertToString(value, type);
					if (strValue == null) {
						continue;
					}
					name = name.substring(3, 4).toLowerCase() + name.substring(4);
					props.put(prefix + name, strValue);
					rc = true;
				} catch (Throwable ignore) {
				}
			}
		}

		return rc;
	}

	private static void checkArguments(Object target, Map<String, Object> props) {
		if (target == null) {
			throw new IllegalArgumentException("target was null.");
		}
		if (props == null) {
			throw new IllegalArgumentException("props was null.");
		}
	}

	/**
	 * 获取指定对象target属性放入map中,并在key中添加指定prefix
	 * 
	 * @param target 指定对象实例
	 * @param prefix 指定属性前缀
	 * @return map 放回map
	 */
	public static Map<String, Object> getProperties(Object target, String prefix) {
		Map<String, Object> map = new HashMap<String, Object>();
		getProperties(target, map, prefix);
		return map;
	}

	/**
	 * 将指定map中属性放入指定对象target
	 * 
	 * @param target 指定对象
	 * @param props 提供属性
	 * @return true 当成功获取一个以上属性
	 */
	public static boolean setProperties(Object target, Map<String, Object> props) {
		boolean rc = false;

		checkArguments(target, props);

		for (Iterator<Entry<String, Object>> iter = props.entrySet().iterator(); iter.hasNext();) {
			Entry<String, Object> entry = iter.next();
			if (setProperty(target, entry.getKey(), entry.getValue())) {
				iter.remove();
				rc = true;
			}
		}

		return rc;
	}

	/**
	 * 将指定map中属性放入对象target,通过prefix属性过滤
	 * 
	 * @param target 指定对象
	 * @param props 提供属性
	 * @param prefix 指定前缀
	 * @return true 当成功获取一个以上属性
	 */
	public static boolean setProperties(Object target, Map<String, Object> props, String prefix) {
		boolean rc = false;

		checkArguments(target, props);

		for (Iterator<String> iter = props.keySet().iterator(); iter.hasNext();) {
			String name = iter.next();
			if (name.startsWith(prefix)) {
				Object value = props.get(name);
				name = name.substring(prefix.length());
				if (setProperty(target, name, value)) {
					iter.remove();
					rc = true;
				}
			}
		}
		return rc;
	}

	/**
	 * 从指定map中提取属性,通过prefix过滤
	 * 
	 * @param props 被提取map
	 * @param prefix 过滤prefix
	 * @return 返回map
	 */
	public static Map<String, Object> extractProperties(Map<String, Object> props, String prefix) {
		if (props == null) {
			throw new IllegalArgumentException("props was null.");
		}
		HashMap<String, Object> rc = new HashMap<String, Object>(props.size());

		for (Iterator<String> iter = props.keySet().iterator(); iter.hasNext();) {
			String name = iter.next();
			if (name.startsWith(prefix)) {
				Object value = props.get(name);
				name = name.substring(prefix.length());
				rc.put(name, value);
				iter.remove();
			}
		}

		return rc;
	}

	/**
	 * 获取指定类的名字,不包含包名
	 * 
	 * @param clazz
	 * @return
	 */
	public static String simpleName(Class<?> clazz) {
		String name = clazz.getName();
		int p = name.lastIndexOf(".");
		if (p >= 0) {
			name = name.substring(p + 1);
		}
		return name;
	}

	public static boolean setProperty(Object target, String name, Object value) {
		try {
			Class<?> clazz = target.getClass();
			Method setter = findSetterMethod(clazz, name);
			if (setter == null) {
				return false;
			}
			// If the type is null or it matches the needed type, just use the value directly
			if (value == null || value.getClass() == setter.getParameterTypes()[0]) {
				setter.invoke(target, new Object[] { value });
			} else {
				// We need to convert it
				setter.invoke(target, new Object[] { convert(value, setter.getParameterTypes()[0]) });
			}
			return true;
		} catch (Throwable ignore) {
			return false;
		}
	}

	private static Object convert(Object value, Class<?> type) throws URISyntaxException {
		PropertyEditor editor = PropertyEditorManager.findEditor(type);
		if (editor != null) {
			editor.setAsText(value.toString());
			return editor.getValue();
		}
		if (type == Date.class) {
			SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			try {
				return dateFormat.parseObject(value.toString());
			} catch (ParseException exception) {
				System.out.println("时间转换异常");
			}

		}
		if (type == Boolean.class) {
			return new Boolean(value.toString().trim());
		}
		if (type == Integer.class) {
			return new Integer(value.toString().trim());
		}
		if (type == URI.class) {
			return new URI(value.toString());
		}

		return null;
	}

	private static String convertToString(Object value, Class<?> type) throws URISyntaxException {
		PropertyEditor editor = PropertyEditorManager.findEditor(type);
		if (editor != null) {
			editor.setValue(value);
			return editor.getAsText();
		}
		if (type == Boolean.class) {
			return ((Boolean) value).toString();
		}
		if (type == Integer.class) {
			return ((Integer) value).toString();
		}
		if (type == URI.class) {
			return ((URI) value).toString();
		}
		return null;
	}

	static Method findSetterMethod(Class<?> clazz, String name) {
		// Build the method name.
		name = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
		Method[] methods = clazz.getMethods();
		for (int i = 0; i < methods.length; i++) {
			Method method = methods[i];
			Class<?> params[] = method.getParameterTypes();
			if (method.getName().equalsIgnoreCase(name) && params.length == 1 && isSettableType(params[0])) {
				return method;
			}
		}
		return null;
	}

	private static boolean isSettableType(Class<?> clazz) {
		if (PropertyEditorManager.findEditor(clazz) != null) {
			return true;
		}
		if (clazz == Date.class) {
			return true;
		}
		if (clazz == URI.class) {
			return true;
		}
		if (clazz == Boolean.class) {
			return true;
		}
		if (clazz == Integer.class) {
			return true;
		}
		return false;
	}

	static public String toString(Object target) {
		return toString(target, Object.class);
	}

	public static String toString(Object target, Class<?> stopClass) {
		LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
		addFields(target, target.getClass(), stopClass, map);
		StringBuffer buffer = new StringBuffer(simpleName(target.getClass()));
		buffer.append(" {");
		boolean first = true;
		for (Entry<String, Object> entry : map.entrySet()) {
			if (first) {
				first = false;
			} else {
				buffer.append(", ");
			}
			buffer.append(entry.getKey());
			buffer.append(" = ");
			appendToString(buffer, entry.getValue());
		}
		buffer.append("}");
		return buffer.toString();
	}

	protected static void appendToString(StringBuffer buffer, Object value) {
		buffer.append(value);
	}

	private static void addFields(Object target, Class<?> startClass, Class<?> stopClass, Map<String, Object> map) {

		if (startClass != stopClass) {
			addFields(target, startClass.getSuperclass(), stopClass, map);
		}

		Field[] fields = startClass.getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			Field field = fields[i];
			if (Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers())
					|| Modifier.isPrivate(field.getModifiers())) {
				continue;
			}

			try {
				field.setAccessible(true);
				Object o = field.get(target);
				if (o != null && o.getClass().isArray()) {
					try {
						o = Arrays.asList((Object[]) o);
					} catch (Throwable e) {
					}
				}
				map.put(field.getName(), o);
			} catch (Throwable e) {
				e.printStackTrace();
			}
		}

	}

}
